package org.budo.dubbo.protocol.http.exporter;

import org.budo.dubbo.protocol.http.Consts;
import org.budo.dubbo.protocol.http.exception.BudoRuntimeException;
import org.budo.dubbo.protocol.http.exporter.util.ExportUtil;
import org.budo.support.lang.util.ArrayUtil;
import org.budo.support.spring.io.util.ResourceUtil;
import org.budo.support.spring.util.SpringUtil;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.io.Resource;

import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
import com.alibaba.dubbo.config.ServiceConfig;

import lombok.Getter;
import lombok.Setter;

/**
 * 以 budo-http 协议暴露 dubbo 服务的工具
 * 
 * @author lmw
 */
@Getter
@Setter
public class BudoDubboHttpExporter implements ApplicationContextAware, InitializingBean {
    private static final Logger log = LoggerFactory.getLogger(BudoDubboHttpExporter.class);

    /**
     * 添加了Sign，替换了Exception
     */
    private String filter = "budo-exception,provider-side-sign,-exception";

    /**
     * 默认开启参数验证
     */
    private String validation = "false";

    private Class<?>[] interfaceTypes = new Class<?>[0];

    private ApplicationContext applicationContext;

    /**
     * 服务端解析请求时候，如果客户端没有指定序列化方式，则用服务端配置的默认方式
     */
    private String serialization = Consts.SerializationDefault; // http调用默认使用json序列化

    private Resource[] interfaceTypeResources;

    /**
     * 暴露服务
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        this.exportService(); // 通过 interfaceTypes 和 interfaceTypeResources 配置的

        // TODO 通过注解配置
    }

    protected void exportService() {
        Class<?>[] interfaceTypesFromResource = ResourceUtil._readLineToTypes(this.getInterfaceTypeResources());
        Class<?>[] allInterfaceTypes = ArrayUtil.concat(this.getInterfaceTypes(), interfaceTypesFromResource);

        if (null == allInterfaceTypes || allInterfaceTypes.length < 1) {
            log.error("#67 interfaceTypes is null or empty, this.getInterfaceTypes()=" + this.getInterfaceTypes() + ", interfaceTypesFromResource=" + interfaceTypesFromResource);
            return;
        }

        for (Class<?> interfaceType : allInterfaceTypes) {
            Object beanRef = SpringUtil.getBean(this.applicationContext, interfaceType);

            if (null == beanRef) {
                log.warn("#76 exportService continue, interfaceType=" + interfaceType + ", beanRef=" + beanRef + ", this=" + this);
                continue;
            }

            this.exportService(interfaceType.getName(), beanRef);
        }
    }

    protected void exportService(String interfaceType, Object beanRef) {
        if (null == interfaceType || null == beanRef) {
            throw new RuntimeException("#84 exportService, interfaceType=" + interfaceType + ", beanRef=" + beanRef);
        }

        try {
            ServiceConfig<Object> serviceConfig = ExportUtil.newServiceConfig(interfaceType, beanRef);
            serviceConfig.setValidation(this.getValidation());
            serviceConfig.setFilter(this.getFilter());

            serviceConfig.export();

            log.info("#85 exported, interfaceType=" + interfaceType + ", beanRef=" + beanRef.getClass() + "@" + beanRef.hashCode() + ", this=" + this);
        } catch (Throwable e) {
            log.error("#82 exportService error, interfaceType=" + interfaceType + ", e=" + e, e);
            throw new BudoRuntimeException("#83 exportService error, interfaceType=" + interfaceType + ", e=" + e, e);
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public String toString() {
        return super.toString() + ", interfaceTypeResources=" + this.getInterfaceTypeResources() + ", interfaceTypes=" + this.getInterfaceTypes();
    }
}